package ikelib;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import wcg.winchk;


public class wintools {
	/**
	 * チャネルブロックサイズを返す
	 * @param data
	 * @param index
	 * @return
	 */
	public static int getChannelBlockSize(byte[] c, int index){
		int chheader = unsigned_byte.convShort(c[index+2])*0x100
		 + unsigned_byte.convShort(c[index+2+1]);	
		int smpl = chheader & 0xfff;
		int ssiz = (chheader>>12) & 0x0f; 
		int dlen=0;
		if(ssiz == 0)	dlen = 4 + smpl / 2;
		else    		dlen = 4 + (smpl-1) * ssiz;
		return dlen + 4; 
	}
	/**
	 * win形式のブロックサイズ(4バイト長）を作成する
	 * @param num
	 * @return
	 */
	public static byte[] getBlockSize(int num){
		return byteUtil.conv4UByte(num);
	}
	/**
	 * winの秒ヘッダを作成する
	 * @param wt
	 * @return
	 */
	public static byte[] makeSecHeader(wintime wt){
		return wt.getBcd();
	}
	/**
	 * win形式にエンコード（圧縮）する
	 * @param rd
	 * エンコードすべきデータ（１秒分）
	 * @param channelID
	 * チャネルID
	 * @return
	 * エンコードされたチャネルブロックを返す
	 */
	public static byte[] makeChEncodeData(int[] rd,int channelID){
		
		int max = Integer.MIN_VALUE;
		int min = Integer.MAX_VALUE;
		for(int i=0;i<rd.length-1;i++){
			int delta = rd[i+1]-rd[i];
			if(max < delta) max = delta;
			if(min > delta) min = delta;
		}
		int enc ;
		if(max > 0x7fffff || min < -0x800000)  enc = 4;
		else if(max > 0x7fff || min < -0x8000) enc = 3;
		else if(max > 0x7f || min < -0x80) enc = 2;
		else if(max > 0x7 || min < -0x8) enc = 1;
		else enc = 0;
		int dlength;
		if(enc == 0) dlength = 4 + (rd.length) / 2;
		else         dlength = 4 + (rd.length -1)*enc;
		
		
		byte[] ret = new byte[dlength+4];
		byteUtil.conv2UByte(channelID,ret,0);
		int encandlen = (rd.length & 0xfff) | (enc<<12) & 0xf000;
		byteUtil.conv2UByte(encandlen,ret,2);
		
		int index = 0;
		byteUtil.conv4Byte(rd[index],ret,4);
		index++;
		for(int bi=4+4;index<rd.length;index++){
			switch(enc){
			case 4: 
				byteUtil.conv4Byte(rd[index] - rd[index-1], ret, bi);
				bi += 4;
				break;
			case 3: 
				byteUtil.conv3Byte(rd[index] - rd[index-1], ret, bi);
				bi += 3;
				break;
			case 2: 
				byteUtil.conv2Byte(rd[index]- rd[index-1], ret, bi);
				bi += 2;
				break;
			case 1: 
				byteUtil.convByte(rd[index]  - rd[index-1], ret, bi);
				bi += 1;
				break;
			case 0: 
				byteUtil.convHalfByte(rd[index]  - rd[index-1], ret, bi,false);
				index++;
				if(rd.length>index) byteUtil.convHalfByte(rd[index]  - rd[index-1], ret, bi,true);
				bi += 1;
				break;
			}
		}
		return ret;
	}
	
	/**
	 * winファイルデータ全体を与えて、１秒winチャンクの集まりに変換する。
	 * @param data　winファイルデータ全体
	 * @param secChunk　秒チャンクデータのリスト
	 * @param winTimeValue　秒データ
	 * @return　
	 */
	public static boolean convertSecChunk(byte[] data,TreeMap<Integer,byte[]> secChunk,TreeSet<wintime> winTimeValue){
		int fmt = winchk.getFileFormat(data, data.length);
		if(fmt == winchk.WIN_FORMAT_A0){
			SecondBlock blk;
			for(int offset=0;offset<data.length;offset += blk.length()){
				blk = new SecondBlock(data,offset);
				wintime tm = blk.getWinTime();
				//System.out.println(tm.fmt());
				winTimeValue.add(tm);
				byte[] chunk = new byte[blk.length()];
				System.arraycopy(data, offset, chunk, 0, blk.length());
				secChunk.put(tm.getValueS(),chunk);
			}
			return true;
		}
		else if(fmt == winchk.WIN_FORMAT_A5){
			SecondBlock blk;
			TreeMap<Integer,SecondBlock> lst = new TreeMap<Integer,SecondBlock>();
			for(int offset=0;offset<data.length;offset += blk.length()){
				blk = new SecondBlock(data,offset,5);
				wintime tm = blk.getWinTime();
				// 必ず０ｍｓから９００ｍｓがそろっていなければいけない
				if(tm.msec()==0){
					lst.clear();
				}
				lst.put(tm.msec(),blk);
				if(tm.msec()==900){
					byte[] chunk = makeSecondChunk(lst);
					if(chunk!=null){
						winTimeValue.add(tm);
						secChunk.put(tm.getValueS(),chunk);
					}
				}
			}
			return true;
		}
		return false;
	}
	/**
	 * １０個の0.1秒ブロックから、一秒分のデータブロックを作る
	 * @param lst
	 * @return
	 */
	private static byte[]  makeSecondChunk(TreeMap<Integer,SecondBlock> lst){
		Set<Integer> key = lst.keySet();
		
		Iterator<Integer> ite = key.iterator();
		boolean f = true;
		
		// 時間連続性のチェック、ｃｈの変化がないかどうかチェック
		wintime tmfirst = new wintime();
		int korg=0;
		ArrayList<Integer> chs = new ArrayList<Integer>();// CHのリスト
		while(ite.hasNext()){
			int k = ite.next();
			SecondBlock b = lst.get(k);
			ArrayList<ChannelData> cdlst = b.getChannelDataList();
			if(f){
				f=false;
				tmfirst = new wintime(cdlst.get(0).getTime());
				for(int i=0;i<cdlst.size();i++){
					chs.add(cdlst.get(i).getChannelName());
				}
			}
			else{
				if(k- korg !=100){ // 100ms毎になっているかチェック
					System.err.println(String.format("win A5 file error : %s :  irregal time interval %d ms to %d ms" ,cdlst.get(0).getTime().fmt(),korg,k));
					return null;
				}
				for(int i=0;i<cdlst.size();i++){
					if(!chs.contains(cdlst.get(i).getChannelName())){
						System.err.println("win A5 file error : " + cdlst.get(0).getTime().fmt() +  ": channel changed " + cdlst.get(i).getChannelName());
						return null;
					}
				}
			}
			korg = k;
		}
		// １秒ブロックデータをつくる
		int blocksize=4+6;// block size=4,sec header=6
		ArrayList<byte[]> d = new ArrayList<byte[]>();
		Iterator<Integer> ite3 = chs.iterator();
		while(ite3.hasNext()){
			int ch = ite3.next();
			int[] sd  = sumChannelData(lst,ch);
			byte[] enc = wintools.makeChEncodeData(sd, ch);
			d.add(enc);
			blocksize+=enc.length;
		}
		// フレームデータを組立
		int len = 0;
		ArrayList<byte[]> d3 = new ArrayList<byte[]>();
		if(blocksize>=14){
			byte[] fdat=wintools.getBlockSize(blocksize);
			d3.add(fdat);
			len += fdat.length;
			byte[] tmdat = wintools.makeSecHeader(tmfirst);
			d3.add(tmdat);
			len += tmdat.length;
			Iterator<byte[]> ite4=d.iterator();
			while(ite4.hasNext()){
				byte[] ndata = ite4.next();
				d3.add(ndata);
				len+=ndata.length;
			}
		}
		// 配列にコピー
		byte[] ret =new byte[len];
		int index=0;
		Iterator<byte[]> ite5 = d3.iterator();
		while(ite5.hasNext()){
			byte[] b = ite5.next();
			System.arraycopy(b, 0, ret, index, b.length);
			index += b.length;
		}
		return ret;
	}
	
	/**
	 * 指定ＣＨのデータを順番にまとめる。
	 * @param lst
	 * @param ch
	 * @return
	 */
	private static int[] sumChannelData(TreeMap<Integer, SecondBlock> lst, int ch) {
		Set<Integer> key = lst.keySet();
		Iterator<Integer> ite = key.iterator();
		ArrayList<int[]> dt = new ArrayList<int[]>();
		int len = 0;
		while(ite.hasNext()){
			int k = ite.next();
			SecondBlock b = lst.get(k);
			ArrayList<ChannelData> cdlst = b.getChannelDataList();
			for(int i=0;i<cdlst.size();i++){
				ChannelData cd = cdlst.get(i); 
				if(cd.getChannelName() == ch){
					dt.add(cd.getData());
					len += cd.getData().length;
				}
			}
		}
		int[] ret = new int[len];
		int index = 0;
		Iterator<int[]> ite2 = dt.iterator();
		while(ite2.hasNext()){
			int[] d = ite2.next();
			System.arraycopy(d, 0, ret, index, d.length);
			index += d.length;
		}
		return ret; 
	}
	
}
